public class EntUtil {
    
    public class PrioritiesMappingException extends Exception {}
    
    public enum TimeType { EFR, EFW, ERT }
    
    public class SPEWrapper {
        
        Map< string, List<Decimal> > priorityMappings = new Map< string, List<Decimal> >();
        string nameStr = '';
        Entitlement__c e;
        Id bhid;
        Boolean bUsesCP = false;
                
        // Assumes:  - Either entId is a valid entitlement Id, or it is Null
        public SPEWrapper(Id entId) {
            this.e = getSPEnt(entId);
            if(this.e!=null) {
                this.priorityMappings = GetSPRequirementsByPriorityFromSObj(e); 
                this.nameStr += this.e.Support_Program__r.Name;
                this.bhid = e.Business_Hours__c;
                this.bUsesCP = e.Case_Pack__c;
            }
        }
        
        public string getMappingsDump() {
            string s = '"'+nameStr+'" => [[ ';
            List<String> keys = new List<String>();
            keys.addAll( priorityMappings.keySet().clone() );
            while (!keys.isEmpty()) {
                string k = keys.remove(0);
                List<Decimal> d = priorityMappings.get(k);
                string defrS, defwS, dertS;
                decimal defr = d[TimeType.EFR.ordinal()];
                if (defr==null) defrS = 'null';
                else defrS = defr.stripTrailingZeros().toPlainString();
                if (d[TimeType.EFW.ordinal()] == null) defwS = 'null';
                else defwS =  d[TimeType.EFW.ordinal()].stripTrailingZeros().toPlainString();
                if (d[TimeType.ERT.ordinal()] == null) dertS = 'null'; 
                else dertS = d[TimeType.ERT.ordinal()].stripTrailingZeros().toPlainString();
                string curr = '\'' + k + '\':{' + defrS + ',' + defwS + ',' + dertS +  '} '; 
                s += curr;
                if (keys.isEmpty()) break;
                else s += ', ';
            }
            return s+']]';
        }
                
        public string getName() { return this.nameStr; }
        
        public boolean hasMapping(string pname) { 
            try { decimal d = this.getEntitledTime(TimeType.EFR,pname); }
            catch(PrioritiesMappingException ex) { return false; }
            return true;
        }

        public Decimal getEntitledTime(TimeType tt, string strPriority) {
            if ((strPriority!=null)&&(this.priorityMappings.containsKey((strPriority.toLowerCase())))) {
                system.debug('Looking up priority:' + strPriority);
                system.debug('  > ' +  getMappingsDump() );
                List<Decimal> pmapValue = this.priorityMappings.get(strPriority.toLowerCase());
                if ( pmapValue==null || pmapValue.get(0)==null || pmapValue.get(1)==null || pmapValue.get(2)==null) {
                    throw new PrioritiesMappingException();
                }
                return pmapValue[ tt.ordinal() ];
            } else if (this.priorityMappings.containsKey('*')) {
                return this.priorityMappings.get('*')[ tt.ordinal() ];
            } else {
                throw new PrioritiesMappingException();
            }
        }
        
        public Long getEntitledTimeInMillis(TimeType tt, string strPriority) { return ((this.getEntitledTime(tt,strPriority)*1000*60*60).longValue()); }
        
        public Id getBHId() { return this.bhid; }
        
        public Boolean UsesCasePack() { return this.bUsesCP; }
    }
    
    
    static testMethod void testGetMappingsDumpWithNullEntitlement() {
        Id nullEntId = null;
        SPEWrapper spe = new SPEWrapper(nullEntId);
        spe.getMappingsDump();
    }
    
    
    static Map< string, List<Decimal> > GetSPRequirementsByPriorityFromSObj(Entitlement__c e) {
        Map< string, List<Decimal> > spReqs = new Map< string, List<Decimal> >();
        if (e!=null) {
            string p1s = (e.Support_Program__r.P1__c==null) ? '' : (e.Support_Program__r.P1__c).toLowerCase();
            string p2s = (e.Support_Program__r.P2__c==null) ? '' : (e.Support_Program__r.P2__c).toLowerCase();
            string p3s = (e.Support_Program__r.P3__c==null) ? '' : (e.Support_Program__r.P3__c).toLowerCase();
            string p4s = (e.Support_Program__r.P4__c==null) ? '' : (e.Support_Program__r.P4__c).toLowerCase();
            string p5s = (e.Support_Program__r.P5__c==null) ? '' : (e.Support_Program__r.P5__c).toLowerCase();
            
            Decimal[] reqs1 = new Decimal[]{ e.Support_Program__r.P1_Response_Hours__c, e.Support_Program__r.P1_Workaround_Hours__c, e.Support_Program__r.P1_Resolution_Hours__c };
            spReqs.put( p1s, reqs1 );
            Decimal[] reqs2 = new Decimal[]{ e.Support_Program__r.P2_Response_Hours__c, e.Support_Program__r.P2_Workaround_Hours__c, e.Support_Program__r.P2_Resolution_Hours__c };
            spReqs.put( p2s, reqs2 );
            Decimal[] reqs3 = new Decimal[]{ e.Support_Program__r.P3_Response_Hours__c, e.Support_Program__r.P3_Workaround_Hours__c, e.Support_Program__r.P3_Resolution_Hours__c };
            spReqs.put( p3s, reqs3 );
            Decimal[] reqs4 = new Decimal[]{ e.Support_Program__r.P4_Response_Hours__c, e.Support_Program__r.P4_Workaround_Hours__c, e.Support_Program__r.P4_Resolution_Hours__c };
            spReqs.put( p4s, reqs4 );
            Decimal[] reqs5 = new Decimal[]{ e.Support_Program__r.P5_Response_Hours__c, e.Support_Program__r.P5_Workaround_Hours__c, e.Support_Program__r.P5_Resolution_Hours__c };
            spReqs.put( p5s, reqs5 );
        }
        return spReqs;
    }

    public static Entitlement__c getSPEnt(Id entId){
        Entitlement__c entRet;
        if(entId==null) { return null; }        
        
        ////////////// NEW VERSION ACTUALLY USES PREVIOUS BULK QUERY ///////////
        try {
        	entRet = CaseInit.mapEntSP.get(entId);
        } catch(Exception ex) {
        	System.debug('\n\nWarning:  Bulk Entitlments query missed a record\n\n' + ex.getMessage() );
        }
        System.assertNotEquals(null,entRet,'SP/Ent missing from bulk query');
        return entRet;
        /////////////////////////////////////////////////////
     
/*        
        /////////// OLD LEGACY VERSION ///////////////////////
        try {
            if(entId==null) { return null; }
            
            entRet = [select Business_Hours__c,Case_Pack__c,
                Support_Program__r.Name,
                Support_Program__r.P1__c,
                Support_Program__r.P1_Resolution_Hours__c,
                Support_Program__r.P1_Response_Hours__c,
                Support_Program__r.P1_Workaround_Hours__c,
                Support_Program__r.P2__c,
                Support_Program__r.P2_Resolution_Hours__c,
                Support_Program__r.P2_Response_Hours__c,
                Support_Program__r.P2_Workaround_Hours__c,
                Support_Program__r.P3__c,
                Support_Program__r.P3_Resolution_Hours__c,
                Support_Program__r.P3_Response_Hours__c,
                Support_Program__r.P3_Workaround_Hours__c,
                Support_Program__r.P4__c,
                Support_Program__r.P4_Resolution_Hours__c,
                Support_Program__r.P4_Response_Hours__c,
                Support_Program__r.P4_Workaround_Hours__c,
                Support_Program__r.P5__c,
                Support_Program__r.P5_Resolution_Hours__c,
                Support_Program__r.P5_Response_Hours__c,
                Support_Program__r.P5_Workaround_Hours__c
                from Entitlement__c where id=:entId][0];
        } catch(Exception e) {
            entRet = null;
        }
        return entRet;
        /////////// OLD LEGACY VERSION ///////////////////////
*/              
    }

    
    public static testMethod void testGetSPRequirementsByPriorityFromSObj() {
        List<string> pNames = new string[]{ 'first', 'second', 'third' ,'fourth' };
        List<decimal> hrs = new decimal[]{  5,6,7,  2,56,67,  55,66,77,  75,76,87,  0,0,0  };
        Entitlement__c e = MakeSPEnt(pNames,hrs,true); 
/*
        SPEWrapper spe = new SPEWrapper(e.Id,new decimal[]{999,999,999});
        integer i = 0;
        for (integer j = 0; j < pNames.size(); j++) {
            decimal efr = spe.getEntitledTime( TimeType.EFR, pNames[j] );
            decimal efw = spe.getEntitledTime( TimeType.EFW, pNames[j] );
            decimal ert = spe.getEntitledTime( TimeType.ERT, pNames[j] ); 
            system.assert( efr  ==  hrs[i+0] );
            system.assert( efw ==  hrs[i+1] );
            system.assert( ert  ==  hrs[i+2] );
            i += 3;
        }
        system.debug( spe.getMappingsDump() ); 
*/
    }
    
    public static Entitlement__c MakeSPEnt(String[] PriorityNames, Decimal[] hrs, boolean committ) {
        system.assert(PriorityNames.size()<=5);
        system.assert(hrs.size()==15);
        List<string> pNames = new List<string>();
        pNames.addAll(PriorityNames);
        while (pNames.size() < 5) {
            pNames.add('');
        }
        Support_Program__c sp = new Support_Program__c(Name='sptest',RecordTypeId=RT.MASPId,
            P1__c=pNames[0],P2__c=pNames[1],P3__c=pNames[2],P4__c=pNames[3],P5__c=pNames[4],
            P1_Response_Hours__c = hrs[0], P1_Workaround_Hours__c = hrs[1], P1_Resolution_Hours__c = hrs[2],
            P2_Response_Hours__c = hrs[3], P2_Workaround_Hours__c = hrs[4], P2_Resolution_Hours__c = hrs[5],
            P3_Response_Hours__c = hrs[6], P3_Workaround_Hours__c = hrs[7], P3_Resolution_Hours__c = hrs[8],
            P4_Response_Hours__c = hrs[9], P4_Workaround_Hours__c = hrs[10], P4_Resolution_Hours__c = hrs[11],
            P5_Response_Hours__c = hrs[12], P5_Workaround_Hours__c = hrs[13], P5_Resolution_Hours__c = hrs[14]
            ); 
        Entitlement__c e = new Entitlement__c(Business_Hours__c=UT_SObjectFactory.DefBHId);
        if (committ) {
            insert sp;
            e.Support_Program__c = sp.Id;
            insert e;
        } else {
            e.Support_Program__r = sp;
        }
        return e;
    }

    public static String GetEntStatusForNewCase(Entitlement__c ent){
        String status = '';
        
        if(ent!=null){
            Double casesRemaining, renewalAmount;
            casesRemaining = ent.Cases_Remaining__c==null?0:ent.Cases_Remaining__c;
            renewalAmount = ent.Renewal_Amount__c==null?0:ent.Renewal_Amount__c;
            
            Boolean createNewCases = ent.Case_Pack__c==True && casesRemaining==0;
            if(createNewCases && renewalAmount>0){
                //status = 'Renew Entitlement And Create New Case';
                status = 'RENEW';
            }
            else if((ent.Case_Pack__c==True && casesRemaining>0) || (ent.Case_Pack__c==False)){
                //status = 'New Case From Entitlement';
                status = 'NEW';

            }            
        }    
        return status;
    }
    
    // Decrements the entitlement's Case Pack if it has one
    public static void DecrementCasePack(Id eId){
        Entitlement__c[] entArr = [Select Id, Name, Cases_Remaining__c, Renewal_Amount__c, Case_Pack__c from Entitlement__c where Id=:eId AND Case_Pack__c=True];     
        if(entArr.size() != 1) {
            System.debug('    NOTICE> No Case pack found for Entitlement.');
            return;
        }       
        
        Entitlement__c ent = entArr[0];                        
        try 
        {
            if(ent.Cases_Remaining__c!=null && ent.Cases_Remaining__c>0) {
                ent.Cases_Remaining__c = ent.Cases_Remaining__c - 1;
                update ent;
            }
        } catch (Exception ex){ 
            System.debug('>>> Unhandled exception:  ' + ex.getMessage()); 
        }         
    }
    
    public static testMethod void testDecrementCasePack() {
        Support_Program__c sp = new Support_Program__c(Name='sp1',RecordTypeId=RT.MASPId); insert sp;
        Entitlement__c eCPOn99 = new Entitlement__c(Name='eCPOn',Support_Program__c=sp.Id,
            Cases_Remaining__c=99, Case_Pack__c=True, Renewal_Amount__c=10,Business_Hours__c=UT_SObjectFactory.DefBHId);
        Entitlement__c eCPOff88 = new Entitlement__c(Name='eCPOff',Support_Program__c=sp.Id,
            Cases_Remaining__c=null, Case_Pack__c=False,Business_Hours__c=UT_SObjectFactory.DefBHId);
        Entitlement__c eCPOnNull = new Entitlement__c(Name='eCPOnNull',Support_Program__c=sp.Id,
            Case_Pack__c=True, Cases_Remaining__c=0, Renewal_Amount__c=10,Business_Hours__c=UT_SObjectFactory.DefBHId );
        Entitlement__c eCPOffNull = new Entitlement__c(Name='eCPOffNull',Support_Program__c=sp.Id,
            Case_Pack__c=False,Business_Hours__c=UT_SObjectFactory.DefBHId);
        Entitlement__c[] eArr = new Entitlement__c[]{ eCPOn99,eCPOff88,eCPOnNull,eCPOffNull };
        insert eArr;
        
        DecrementCasePack(eCPOn99.Id);
        DecrementCasePack(eCPOff88.Id);
        DecrementCasePack(eCPOnNull.Id);
        DecrementCasePack(eCPOffNull.Id);
        
        Entitlement__c[] res = [select Cases_Remaining__c from Entitlement__c where Id in :eArr];
        system.assert(res.size()==4);
        system.assert(res[0].Cases_Remaining__c==98);
        system.assert(res[1].Cases_Remaining__c==null);
        system.assertEquals(res[2].Cases_Remaining__c,0.0);

        system.assert(res[3].Cases_Remaining__c==null);
        
    }
/*    
    //
    // Returns the Business Hours Id from Entitlement, or, if null or the Entitlement is invalid, then
    // returns the org default BId
    //
    public static Id getEntitledBHId(Id eId){
        Id bId;
        try {
            bid = [Select Id,Business_Hours__c from Entitlement__c Where Business_Hours__c<>'' And 
                Id=:eId Limit 1][0].Business_Hours__c;
        } catch(Exception e) { bId=null; }
        return BHWithFallback.getSpecifiedOrDefault(bid);
    }
    
    public static testMethod void testGetEntitledBHId() {
        Id bId = getEntitledBHId((Id)null);
        
        Support_Program__c sp = new Support_Program__c( RecordTypeId=RT.MASPId , Name='TEST-SP' ); insert sp;
        Entitlement__c e = new Entitlement__c(Business_Hours__c=UT_SObjectFactory.DefBHId,Name='UnitTestEnt',
            Support_Program__c=sp.Id );
        insert e;
        Id bId2 = getEntitledBHId(e.Id);
    }
*/
}